/*
 * UserNet  --  TCP/IP implementation in userspace
 * Copyright (C) 2010  Felip Moll
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef UNET_TCP_H
#define UNET_TCP_H

#include <stdint.h>
#include <netinet/in.h>		/* Struct in_addr */
#include <netinet/tcp.h>	/* TCP structs */
#include "list.h"
#include "rpc.h"
#include "ip.h"
#include "misc.h"
#include <semaphore.h>
#include <pthread.h>
#include <errno.h>

/* RFC1122 INITIAL VALUES */

/*Connection parameters*/
#define PROBES 6 /*Max number of retries for a packet or ack.*/
#define SND_WSCALE 1 /*Scaling factor, only used if options are enabled*/

/*Timing*/
#define RTT 0   /*Min. time elapsed since we send a packet to a host until
		  we receive his response. Default is infinite speed, 0 seconds*/
#define RTO 10	/*Max time in seconds that we will wait for a packet.
		  Default is 3 seconds, must be calculated dynamicaly based on
		  current RTT.*/
#define ACK_TIMEOUT 10 /*Max time in seconds that we will wait for a ack*/
#define BACKOFF 1; /*Time between retransmitions, in sec*/

/*Data*/
#define UTCP_MSS 1460     //This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
#define UIP_MTU 1500;
#define RECV_BUFF_LEN 65535 //Global Reception buffer capacity in bytes

/* Structures */
typedef struct
{
  /* UNET TCP Connection.
   * It defines the necessary elements that a connection must have
   * in order to save their status.
   * Identifier is <local_port,remote_port>.
   * local_addr: Local IP Address. To calculate checksums.
   * remote_addr: Remote IP Address. To calculate checksums.
   * local_port: Local port number where we receive packets.
   * remote_port: Remote port number where we send packets.
   * tcp_inf: STATUS of the connection.
   * packet_queue: Buffer of this connection.
   *     FIXME: Doesn't have a limit! Controlled with recv_buffer?.. nop, must
   *     control it here
   * sem: Indicates how many packets are in the packet_queue.
   * mutex: When adding a packet to packet_queue, we need to block all reads/writes.
   * element: To insert this connection in the connection_list.
   */
  struct in_addr local_addr;
  struct in_addr remote_addr;
  uint16_t local_port;
  uint16_t remote_port;
  struct tcp_info tcp_inf;
  struct list_head packet_queue;
  sem_t sem; 
  pthread_mutex_t mutex;
  struct list_head element;
} *unet_tcp_connection_t;


struct unet_tcp_listen_node
{
  /* Identifier is <port,listen_addr>
   * port: Port where we must listen for new connection requests.
   * listen_addr: IP Address from where we are listening to.
   * pending_syn_slots: The max number of free slots for connection requests.
   * syn_rcvd_q: Queue of packets received requesting a new conn.
   * mutex: Mutex for this listen_node.
   * sem: Used to warn that a new request has arrived.
   * element: to insert this node into a queue.
   */
  uint16_t port;
  struct in_addr listen_addr; //It's in fact a struct with a in_addr_t == uint32_t
  uint16_t pending_syn_slots;
  struct list_head syn_rcvd_q;
  pthread_mutex_t mutex;
  sem_t sem;
  struct list_head element;
};

struct tcp_options
{
    size_t len; //Lenght in bytes
    uint8_t * options; //Pointer to a options buffer
};

struct unet_tcp_packet
{
  /* Identifier is <header.source,header.dest>. These are ports num.
   * payload: The payload of the TCP segment.
   * payload_size: The size of the payload buffer in bytes.
   * opts: &header->doff*4 - 20 = opts len.
   * s_address: Source IP address of the packet.
   * d_address: Destination IP address of the packet.
   * element: to insert this node into a queue.
   * Note: s_address and d_address are used to calculate the checksum
   * of the packet, as explained in RFC793.
   */
  struct tcphdr header;
  uint8_t * payload;
  size_t payload_size;
  struct tcp_options opt;
  struct in_addr s_address;
  struct in_addr d_address;
  struct list_head element;
};

struct recv_buffer
{
  /* This is the receive packet buffer from the outside (IP)
   * There's a max size for these buffer specified as left_size.
   * It's controlled with RECV_BUFFER_LEN define.
   */
  u_int32_t left_size;
  struct list_head queue;
  pthread_mutex_t mutex;
  sem_t sem;
} recv_buffer;


/* Lists, buffers and other stuff */

static uint8_t tcp_init = 0;
pthread_mutex_t listen_list_mutex, connection_list_mutex;
struct list_head listen_list, connection_list;


/* Signatures */

extern struct unet_tcp_listen_node *unet_tcp_listen (struct in_addr listen_addr, uint16_t port,
						     int max_pending_req);

extern unet_tcp_connection_t unet_tcp_connect (struct in_addr dest_addr,uint16_t src_port,
					       uint16_t dst_port);

extern unet_tcp_connection_t unet_tcp_accept (struct unet_tcp_listen_node *listen_node);
extern size_t unet_tcp_recv (unet_tcp_connection_t conn, void *buf, size_t n);
extern void unet_tcp_deliver(struct in_addr s_addr, struct in_addr d_addr,void * buf, size_t n);
extern void unet_tcp_send (unet_tcp_connection_t conn, const void *buf,
			   size_t n);
extern int unet_tcp_listen_close (struct unet_tcp_listen_node *listen_node);
extern int unet_tcp_conn_close (unet_tcp_connection_t connection);


void tcp_ip_send(unet_tcp_connection_t conn, struct unet_tcp_packet * packet);
u_int32_t get_tcp_time();
u_int16_t tcp_checksum(struct unet_tcp_packet *);
void set_conn_options(struct tcp_options *, struct tcp_info *);

void receive_manager(struct unet_tcp_packet *, size_t);

void free_connection(unet_tcp_connection_t conn);
void free_packet(struct unet_tcp_packet * p);

/* TCP_INIT code */
void unet_tcp_init ();
static inline void TCP_INIT () { if (!tcp_init) unet_tcp_init (); };

/* Auxiliar queue functions */
struct unet_tcp_listen_node *list_head_to_listen_node (struct list_head *l);
struct unet_tcp_packet *list_head_to_utcp_packet (struct list_head *l);
unet_tcp_connection_t *list_head_to_tcp_connection (struct list_head *l);


#endif
